/*
 * net_c.cpp
 *
 *  Created on: Sep 27, 2013
 *      Author: killerg
 */




#include "net_c.h"

#include "share/util.h"

#include <event2/event.h>
#include <event2/bufferevent.h>

tran_c::tran_c()
    :base(NULL)
    ,bev(NULL)
    ,connected(false)
    ,connecting(false)
    ,self_base(false)
    ,addr_len(0)
    ,send_index(INIT_MSG_INDEX)
    ,code_cb(NULL)
     {
}
tran_c::~tran_c() {
}

bool tran_c::init(event_base* base, code_cb_t code_cb, bool nodelay) {
    self_base = false;
    if (NULL == base) {
        base = event_base_new();
        self_base = true;
    }
    this->base = base;
    this->open_cb = NULL;
    this->close_cb = NULL;
    this->code_cb = code_cb;
    this->nodelay = nodelay;

    if (NULL == base) {
        UTIL_LOG(E, "event_base_new");
        return false;
    }

    return true;
}
void tran_c::destroy() {
    disconnect();

    if (NULL != base && self_base) {
        event_base_free(base);
        base = NULL;
        self_base = false;
    }
}
void tran_c::update() {
    if (NULL != base)
        event_base_loop(base, EVLOOP_NONBLOCK);
}

void _event_cb(bufferevent* bev_, short events_, void* trans_) {
    tran_c* trans = (tran_c*)trans_;

    if (events_ & BEV_EVENT_CONNECTED) {
    	int fd = bufferevent_getfd(bev_);
    	if (fd >= 0 && trans->nodelay) {
    		int yes = 1;
            setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(int));
    	}

        trans->connected = true;
        trans->connecting = false;
        if (trans->open_cb)
            trans->open_cb();
    }

    if (events_ & BEV_EVENT_ERROR) {
        trans->disconnect();
    }

    if (events_ & BEV_EVENT_EOF) {
        trans->disconnect();
    }
}

bool tran_c::connect(
        const char* address,
        bool block,
        handler_t open_cb,
        handler_t close_cb,
        handler_t error_cb) {

    if (is_connected()) {
        if (this->address == address)
            return true;
        else
            disconnect();
    }

    if (this->address != address) {
        this->address = address;
        if (this->address.size() == 0) {
            UTIL_LOG(E, "tran_c::connect address:%s empty", address);
            return false;
        }

        addr_len = sizeof(addr);
        if (evutil_parse_sockaddr_port(this->address.c_str(), &addr, &addr_len) < 0) {
            UTIL_LOG(E, "evutil_parse_sockaddr_port");
            return false;
        }
        this->open_cb = open_cb;
        this->close_cb = close_cb;
        this->error_cb = error_cb;
    }


    if (block) {
        while (!is_connected()) {
            if (!is_connecting())
                _try_connect();
            update();
        }
    } else {
        if (!is_connecting())
            _try_connect();
    }

    return true;
}

void tran_c::_try_connect() {
    if (is_connected() || is_connecting())
        return;

    bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
    bufferevent_setcb(bev, NULL, NULL, _event_cb, this);
    bufferevent_enable(bev, EV_READ|EV_WRITE);
    if (bufferevent_socket_connect(bev, &addr, sizeof(addr)) < 0) {
        UTIL_LOG(E, "tran_c::_try_connect err:%s", strerror(errno));
        disconnect();
        return;
    }

    connecting = true;
    connected = false;
    send_index = INIT_MSG_INDEX;
}

void tran_c::disconnect() {
    if (NULL != bev) {
        bufferevent_free(bev);
        bev = NULL;
    }

    if (connected && close_cb)
        close_cb();

    connected = connecting = false;

    UTIL_LOG(T, "client set disconnect");
}
